#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdio.h>
#include "esp_log.h"
#include "cJSON.h"
#include <string.h>
#include "esp_console.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "driver/uart_vfs.h"
#include "driver/uart.h"
#include "esp_vfs_dev.h"
#include "driver/gpio.h"

#define TAG "main"

/**
 * 初始化调试命令行
 * @param 无
 * @return 无
 */
static void initialize_console(void)
{
    /* Drain stdout before reconfiguring it */
    fflush(stdout);
    fsync(fileno(stdout));

    /* Disable buffering on stdin */
    setvbuf(stdin, NULL, _IONBF, 0);

    /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
    uart_vfs_dev_port_set_rx_line_endings(0, ESP_LINE_ENDINGS_CR);
    /* Move the caret to the beginning of the next line on '\n' */
    uart_vfs_dev_port_set_tx_line_endings(0, ESP_LINE_ENDINGS_CRLF);

    /* Configure UART. Note that REF_TICK is used so that the baud rate remains
     * correct while APB frequency is changing in light sleep mode.
     */
    #if 1
    const uart_config_t uart_config = {
            .baud_rate = 115200,
            .data_bits = UART_DATA_8_BITS,
            .parity = UART_PARITY_DISABLE,
            .stop_bits = UART_STOP_BITS_1,
#if SOC_UART_SUPPORT_REF_TICK
        .source_clk = UART_SCLK_REF_TICK,
#elif SOC_UART_SUPPORT_XTAL_CLK
        .source_clk = UART_SCLK_XTAL,
#endif
    };
    /* Install UART driver for interrupt-driven reads and writes */
    
    ESP_ERROR_CHECK( uart_param_config(0, &uart_config) );
    #endif
    ESP_ERROR_CHECK( uart_driver_install(0,
            256, 0, 0, NULL, 0) );
    /* Tell VFS to use UART driver */
    uart_vfs_dev_use_driver(0);

    /* Initialize the console */
    esp_console_config_t console_config = {
            .max_cmdline_args = 8,
            .max_cmdline_length = 256,

    };
    ESP_ERROR_CHECK( esp_console_init(&console_config) );

    /* Configure linenoise line completion library */
    /* Enable multiline editing. If not set, long commands will scroll within
     * single line.
     */
    linenoiseSetMultiLine(1);

    /* Tell linenoise where to get command completions and hints */
    linenoiseSetCompletionCallback(&esp_console_get_completion);
    linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);

    /* Set command history size */
    linenoiseHistorySetMaxLen(100);

    /* Set command maximum length */
    linenoiseSetMaxLineLen(console_config.max_cmdline_length);

    /* Don't return empty lines */
    //linenoiseAllowEmpty(false);
}

/**
 * 调试命令行的任务
 * @param 无
 * @return 无
 */
void cmd_task(void* param)
{
    linenoiseSetDumbMode(1);
    while(true) {
        /* Get a line using linenoise.
         * The line is returned when ENTER is pressed.
         */
        char* line = linenoise("esp>");
        if (line == NULL) { /* Break on EOF or error */
            break;
        }
        /* Add the command to the history if not empty*/
        if (strlen(line) > 0) {
            linenoiseHistoryAdd(line);

        }

        /* Try to run the command */
        int ret;
        esp_err_t err = esp_console_run(line, &ret);
        if (err == ESP_ERR_NOT_FOUND) {
            printf("Unrecognized command\n");
        } else if (err == ESP_ERR_INVALID_ARG) {
            // command was empty
        } else if (err == ESP_OK && ret != ESP_OK) {
            printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
        } else if (err != ESP_OK) {
            printf("Internal error: %s\n", esp_err_to_name(err));
        }
        /* linenoise allocates line buffer on the heap, so need to free it */
        linenoiseFree(line);
    }
}

//打印HELLOWORLD
static int helloworld_cmd(int argc, char **argv)
{
    ESP_LOGI(TAG,"you enter the helloworld cmd");
    
    return 0;
}

//开关LED
static int led_cmd(int argc, char **argv)
{
    if(argc != 2)
    {
        return -1;
    }
    if(strcmp(argv[1],"on") == 0)
    {
        gpio_set_level(GPIO_NUM_15,1);
    }
    else if(strcmp(argv[1],"off") == 0)
    {
        gpio_set_level(GPIO_NUM_15,0);
    }
    return 0;
}

//注册命令
void set_cmd_init(void)
{
    //打印helloworld命令
    const esp_console_cmd_t cmd1 = {
        .command = "helloworld",
        .help = "this command just for test helloworld",
        .hint = NULL,
        .func = helloworld_cmd,
    };
    esp_console_cmd_register(&cmd1);

    //控制LED命令
    const esp_console_cmd_t cmd2 = {
        .command = "led",
        .help = "this command for led test",
        .hint = NULL,
        .func = led_cmd,
    };
    esp_console_cmd_register(&cmd2);
}

void app_main(void)
{
     //启动调试命令行任务
    initialize_console();
    set_cmd_init();

    //初始化LED的gpio口
    gpio_config_t   led_cfg = 
    {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = (1<<GPIO_NUM_15),
    };
    gpio_config(&led_cfg);

    xTaskCreatePinnedToCore(cmd_task, "cmd_task", 4096, NULL,2, NULL, 1);
}
